package controllers

import java.io.File
import actors.{ActorManager, MissionManager}
import akka.stream.Materializer
import akka.stream.scaladsl.{FileIO, StreamConverters}
import command.{CommandExec, MissionUtils}
import controllers.Assets.Asset
import dao._

import javax.inject.Inject
import org.apache.commons.io.FileUtils
import play.api.mvc._
import tool.{FormTool, Tool}
import implicits.Implicits._
import models.Tables._
import org.joda.time.DateTime
import play.api.http.{AcceptEncoding, HttpEntity}
import play.api.i18n.{I18nSupport, Lang}
import play.api.libs.ws.WSClient
import shared.Pojo._
import shared.VarTool
import tool.Pojo._
import tool.plot.AllPlot._
import tool.plot._
import tool.soft.AllSoft._
import tool.soft._
import utils.Utils

import scala.collection.immutable
import scala.collection.immutable.SeqMap
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import upickle.default._

import java.nio.file.Files

/** Created by yz on 14/7/2020
  */
class SoftController @Inject() (cc: ControllerComponents)(
    implicit val modeDao: ModeDao,
    implicit val missionDao: MissionDao,
    implicit val workflowMissionDao: WorkflowMissionDao,
    implicit val hmdbKeggInfoDao: HmdbKeggInfoDao,
    implicit val hmdbOtherNameKeggDao: HmdbOtherNameKeggDao,
    implicit val softDao: SoftDao,
    implicit val collectDao: CollectDao,
    implicit val matrixFileDataDao: MatrixFileDataDao,
    implicit val ws: WSClient,
    implicit val materializer: Materializer,
    implicit val keggInfoDao: KeggInfoDao
) extends AbstractController(cc)
    with I18nSupport {

  implicit val dao = MyDao(
    missionDao,
    modeDao,
    hmdbKeggInfoDao,
    hmdbOtherNameKeggDao,
    workflowMissionDao,
    matrixFileDataDao,
    keggInfoDao = keggInfoDao
  )
  implicit val messages = messagesApi.preferred(List(Lang("en")))

  val actorManager = akka.actor.typed.ActorSystem[Nothing](ActorManager(), "actorManager")

  import shared.pojo.Pojo.soft._

  def toIndex = Action { implicit request =>
    Ok(views.html.user.soft.index())
  }

  def getFileContent = Action { implicit request =>
    val data = FormTool.exampleForm.bindFromRequest().get
    val curExampleDir = new File(Tool.softExampleDir, data.kind)
    val file = new File(curExampleDir, data.fileName)
    val str = file.str
    Ok(str)
  }

  def downloadExampleData = Action { implicit request =>
    val data = FormTool.fileNameForm.bindFromRequest().get
    val kind = FormTool.kindForm.bindFromRequest().get.kind
    val exampleDir = Tool.softExampleDir
    val kindDir = new File(exampleDir, kind)
    val file = new File(kindDir, data.fileName)
    Ok.sendFile(file)
      .withHeaders(
        CONTENT_DISPOSITION -> s"${Tool.getContentDisposition(file.getName)}",
        CONTENT_TYPE -> "application/x-download"
      )
  }

  def preDealBefore = Action { implicit request =>
    val kind = PreDealSoft.nameStr
    val missionName = s"${kind}_${Tool.generateMissionName}"
    Ok(views.html.user.soft.preDeal(missionName, None))
  }

  def preprocessBefore = Action { implicit request =>
    val kind = PreprocessSoft.nameStr
    val missionName = s"${kind}_${Tool.generateMissionName}"
    Ok(views.html.user.soft.preprocess(missionName, None))
  }

  def annoBefore = Action { implicit request =>
    val kind = AnnoSoft.nameStr
    val missionName = s"${kind}_${Tool.generateMissionName}"
    Ok(views.html.user.soft.anno(missionName))
  }

  def pcaBefore = Action { implicit request =>
    val kind = PcaSoft.nameStr
    val missionName = s"${kind}_${Tool.generateMissionName}"
    Ok(views.html.user.soft.pca(missionName))
  }

  def vennBefore = Action { implicit request =>
    val kind = VennSoft.nameStr
    val missionName = s"${kind}_${Tool.generateMissionName}"
    Ok(views.html.user.soft.venn(missionName))
  }

  def getAllSoft = Action.async { implicit request =>
    softDao.selectAll.map { x =>
      val array = Utils.getArrayByTs(x)
      Ok(writeJs(array))
    }
  }

  def collect = Action.async { implicit request =>
    val userId = Tool.getUserId
    val data = FormTool.nameForm.bindFromRequest().get
    val collectF = collectDao.select(userId).flatMap { collect =>
      val softs = data.name :: collect.softs
      val newRow = collect.copy(softs = softs)
      collectDao.update(newRow)
    }
    val softF = softDao.select(data.name).flatMap { row =>
      val newRow = row.copy(heart = row.heart + 1)
      softDao.update(newRow)
    }
    collectF.zip(softF).map { x =>
      Ok(writeJs("success"))
    }
  }

  def unCollect = Action.async { implicit request =>
    val userId = Tool.getUserId
    val data = FormTool.nameForm.bindFromRequest().get
    val collectF = collectDao.select(userId).flatMap { collect =>
      val softs = collect.softs.filter(x => x != data.name)
      val newRow = collect.copy(softs = softs)
      collectDao.update(newRow)
    }
    val softF = softDao.select(data.name).flatMap { row =>
      val newRow = row.copy(heart = row.heart - 1)
      softDao.update(newRow)
    }
    collectF.zip(softF).map { x =>
      Ok(writeJs("success"))
    }
  }

  def diffAnaBefore = Action { implicit request =>
    val kind = DiffAnaSoft.nameStr
    val missionName = s"${kind}_${Tool.generateMissionName}"
    Ok(views.html.user.soft.diffAna(missionName))
  }

  def lefseAnaBefore = Action { implicit request =>
    val kind = LefseAnaSoft.nameStr
    val missionName = s"${kind}_${Tool.generateMissionName}"
    Ok(views.html.user.soft.lefseAna(missionName))
  }

  def limmaAnaBefore = Action { implicit request =>
    val kind = LimmaAnaSoft.nameStr
    val missionName = s"${kind}_${Tool.generateMissionName}"
    Ok(views.html.user.soft.limmaAna(missionName))
  }

  def diffAnaMulBefore = Action { implicit request =>
    val kind = DiffAnaMulSoft.nameStr
    val missionName = s"${kind}_${Tool.generateMissionName}"
    Ok(views.html.user.soft.diffAnaMul(missionName))
  }

  def fileNameCheck = Action.async { implicit request =>
    val data = FormTool.fileNameForm.bindFromRequest.get
    val userId = Tool.getUserId
    Tool.produceRawDataFileF(userId, data.fileName).map { file =>
      if (file.exists()) {
        Ok(ujson.Obj("valid" -> true))
      } else {
        Ok(ujson.Obj("valid" -> false))
      }
    }
  }

  def checkFileName = Action.async { implicit request =>
    val data = Tool.transformRq.transform(reader[CheckFileName.Request])
    Tool.produceRawDataFileF(data.id, data.fileName).map { file =>
      if (file.exists()) {
        Ok(ujson.Obj("valid" -> true))
      } else {
        Ok(ujson.Obj("valid" -> false))
      }
    }
  }

  def ccaRdaBefore = Action { implicit request =>
    val kind = CcaRdaSoft.nameStr
    val missionName = s"${kind}_${Tool.generateMissionName}"
    Ok(views.html.user.soft.ccaRda(missionName))
  }

  def heatmapBefore = Action { implicit request =>
    val kind = HeatmapSoft.nameStr
    val missionName = s"${kind}_${Tool.generateMissionName}"
    Ok(views.html.user.soft.heatmap(missionName))
  }

  def plsdaBefore = Action { implicit request =>
    val kind = PlsdaSoft.nameStr
    val missionName = s"${kind}_${Tool.generateMissionName}"
    Ok(views.html.user.soft.plsda(missionName))
  }

  def analysisBefore = Action { implicit request =>
    val kind = FormTool.kindForm.bindFromRequest().get.kind
    val curSoft = Tool.kindSoftMap(kind)
    val missionName = s"${kind}_${Tool.generateMissionName}"
    val html = curSoft.getHtml(missionName)
    Ok(html)
  }

  def analysis = Action.async { implicit request =>
    val kind = FormTool.kindForm.bindFromRequest().get.kind
    val curSoft = Tool.kindSoftMap(kind)
    val row = curSoft.getMission
    missionDao.insert(row).flatMap(_ => missionDao.select(row.userId, kind, row.missionName)).flatMap { mission =>
      val outDir = Tool.getUserMissionKindDir(kind)
      val missionDir = MissionUtils.getMissionDir(mission.id, outDir)
      val (workspaceDir, resultDir) = (missionDir.workspaceDir, missionDir.resultDir)
      curSoft.produceConfigFile(workspaceDir, 0)
      curSoft.produceInputFile(workspaceDir)
      val newMission = mission.copy(state = "wait")
      missionDao.update(newMission).map { x =>
        Ok(ujson.Obj("valid" -> true))
      }
    }
  }

  def oplsdaBefore = Action { implicit request =>
    val kind = OplsdaSoft.nameStr
    val missionName = s"${kind}_${Tool.generateMissionName}"
    Ok(views.html.user.soft.oplsda(missionName))
  }

  def basicBefore = Action { implicit request =>
    val kind = BasicSoft.nameStr
    val missionName = s"${kind}_${Tool.generateMissionName}"
    Ok(views.html.user.soft.basic(missionName))
  }

  def video = Action { implicit request =>
    val data = Tool.transformRq.transform(reader[FileNameData])
    val file = new File(Tool.videoDir, data.fileName)
    val source = FileIO.fromPath(file.toPath)
    val filePath = file.toPath
    RangeResult.ofSource(
      entityLength = Files.size(filePath),
      source = source,
      rangeHeader = request.headers.get(RANGE),
      fileName = None,
      contentType = Some("video/mp4")
    )
  }

}
